package cs236369.webserver;

import java.util.LinkedList;
import java.util.UUID;

import Logging.Monitor;

/**
 * This class is an implementation to a generic thread-pool. 
 * A client wanting to use this class should supply a Runnable object to the 
 * execute method. The passed object will be run by one of the threads.
 * Handling:
 * <ol>
 * <li>finite queue</li> 
 * <li>not loosing threads</li>
 * </ol>
 * @author ron
 */
public class ThreadPool
{
    private final int nThreads;
    private final PoolWorker[] threads;
    private final int queueSize;
    private final LinkedList<Runnable> queue;

    public ThreadPool(int nThreads,int queueSize)
    {
        this.nThreads = nThreads;
        this.queueSize = queueSize;
        queue = new LinkedList<Runnable>();
        threads = new PoolWorker[nThreads];
    }
    
    /**
     * Starts the thread
     */
    public void start() {
    	
    	// starting off the threads 
    	Monitor.write(this, "Making our threads run.");
    	for (int i=0; i<nThreads; i++) {
    		threads[i] = new PoolWorker();
    		threads[i].start();
    	}
    }

    /**
     * This method adds a Runnable operation to our queue of tasks
     * @param r will be run by a thread from our thread-pool
     * @throws Exception in case the queue of tasks are full
     */
    public void addTask(Runnable r) throws Exception {
        synchronized(queue) {
        	if(queue.size() == queueSize) {
        		throw new Exception("The server is currently busy satisfing " +
        									"the other clients requests" );
        	}
        	Monitor.write(this, "Adding a task to the queue");
            queue.addLast(r);
            queue.notify();
        }
    }
    
    /**
     * Stops all the current threads 
     */
    public void shoutdown() {
    	
    	for (Thread thread : threads) {
			thread.interrupt();
		}
    }

    /**
     * 
     * @author ron
     */
    private class PoolWorker extends Thread {
    	
    	UUID id = UUID.randomUUID();
    	
        public void run() {
            Runnable r;

            while (true) {
                synchronized(queue) {
                    while (queue.isEmpty()) {
                        try
                        {
                            queue.wait();
                        }
                        catch (InterruptedException ignore){}
                    }

                    Monitor.write(this, "Thread:"+id+" received a task");
                    r = queue.removeFirst();
                }

                // catching RuntimeException so the pool couldn't leak threads
                try {
                    r.run();
                } catch (RuntimeException e) {
                    // TODO something
                }
            }
        }
    } // PoolWorker
    
}